// 测试案例
// let a = { // init
//   a:1,
//   b:2,
//   c:{
//     name:'huong',
//     id:1,
//     func: function() {console.log(3333)},
//     d:{
//       partent:'fax'
//     },
//     g:[1,2,3],
//     h:[{j:1},{k:2}, {b:4}]
//   }
// }


// let b = { //custom
//   a:3,
//   c: {
//     name2: 'huooong',
//     id:3,
//     func: function() {console.log(222)},
//     d: {
//       parent2:'fax22'
//     },
//     g:[4],
//     h:['enum', {k:4, j:2, targetIndex: [1]}, {targetIndex: [2], }, {d:'asfasd'}]
//   }
// }

// 结果是 b 中的数据会 混入 数据 a 中

class MixinChartsOptions {
  constructor() {
    this.enumerateTypes = ['all', 'enum'] // 枚举的类型
  }

  variableType(target) {
    return Object.prototype.toString.call(target).replace(/\[object\s(\w+)\]/,'$1')
  }

  isArray(target) {
    return this.variableType(target) === 'Array'
  }

  isFunc(target) {
    return this.variableType(target) === 'Function'
  }

  // 混合自定义配置
  mixinOptions(origin, custom) {
    const customKeys = Object.keys(custom).filter(key => Object.prototype.hasOwnProperty.call(origin, key)) // 取出对象中，相同的key
    const types = ['Array', 'Object']
    let result = {}
    for(let i = 0; i<customKeys.length; i++) {  // 对相同的key进行循环
      const key = customKeys[i]
      const originV = origin[key] // 原始的配置对象
      const customV = custom[key] // 自定义的配置对象
      // 对比
      const originType = this.variableType(originV) // 获取原始配置中的对象的类型
      const isComplex = types.includes(originType) // 数组 or 对象
      if(isComplex) { // 如果是复杂类型,需要执行递归。场景为，如果对象中还存在对象，那么也需要对这些对象进行比对
        if(this.variableType(customV) === 'Array') {  // 如果当前的对象是数组
          this.isEnumArray(customV) ? 
            result[key] = [...this.runEnum(originV, customV)] :   // 执行数组对象的配置融合
            result[key] = customV // 否则一般对象直接用自定义配置 替换 默认配置
          continue
        }
        result[key] = this.mixinOptions(origin[key], custom[key]) // 一般对象，直接递归执行
        continue
      }
    }
    return {...origin, ...custom, ...result}  // 结束条件为原始的配置对象 和 自定义的配置对象均为基础类型。按照 混合对象 > 自定义配置对象 > 原始的对象配置 形式混合
  }
  // 数组对象的融合配置 all 模式
  all(originV, customV) { // 直接获取配置中的第一个作为默认的自定义配置
    return originV.reduce((pre, cur) => {
      pre.push(this.mixinOptions(cur, customV[0]))
      return pre
    }, [])
  }
  // 数组对象的融合配置 enum 模式
  enum(originV, customV) {
    // 如果有targetIndex, 则优先混合index的对象
    const changedList = []  // 已经修改过的下标值（用于最后使用默认融合的时候，避免重复修改）
    const result = [] // 处理结果
    customV.sort((a, b) => a.targetIndex - b.targetIndex) // 先排序，让有targetIndex的排在最前面
    customV.forEach((item) => {
      if(item.targetIndex) {  // 有targetIndex 并且 是数组
        let targetIndexList = item.targetIndex
        if(this.isFunc(targetIndexList)) {  // 如果是方法，优先执行一遍。结果只有返回的是数组用于后续的逻辑执行
          targetIndexList = item.targetIndex()
        }
        if(this.isArray(targetIndexList)) {  // 是数组
          this.enumSpliceOriginV(item.targetIndex, changedList, originV, item)  // 单独根据targetIndex的下标修改对应的对象
        }
      } else {  // 默认没有targetIndex为默认配置
        this.enumReduceResult(result, changedList, originV, item) // 默认的遍历循环所有结果
      }
    })
    return result
  }

  // 枚举的过程中通过遍历去修改对应的配置
  enumSpliceOriginV(targetIndex, changedList, originV, customV) {
    targetIndex.forEach(idx => {
      const index = idx - 1
      changedList.push(index) // 保存每一个targetIndex下标，避免最后重复修改
      originV.splice(index, 1, this.mixinOptions(originV[idx], customV))
    })
  }

  // 最终遍历枚举的所有结果
  enumReduceResult(result, changedList, originV, customV) {
    originV.reduce((pre, cur, index) => {
      if(cur.targetIndex) { // 移除标签
        delete cur.targetIndex
      }
      changedList.includes(index) ? pre.push(cur) : pre.push(this.mixinOptions(cur, customV)) // 只有没有修改过的下标才会混合，否则直接保存本身
      return pre
    }, result)
  }
  // 用于判断是否属于数组对象配置模式
  isEnumArray(customV) {
    const first = customV.slice(0, 1)[0]
    const fType = this.variableType(first)
    if(fType === 'String' && this.enumerateTypes.includes(first)) return true
  }

  // 执行配置的枚举操作
  runEnum(originV, customV) {
    const type = customV.slice(0 ,1)  // 获取第一个为枚举的类型
    const customValued = customV.slice(1) // 浅拷贝一份数据
    return this[type](originV, customValued) // 根据不同类型执行不同的枚举操作
  }
}

module.exports = {
  MixinChartsOptions
}